<?php
include_once 'utils.inc';
$wptResultsDir = dirname(__FILE__) . "/wpt_results/";

/**
 * @param  $id 'The wpt result ID'
 * @param  $xmlResults 'The contents of the result'
 * @return void
 */
function saveWPTXmlResults($id, $xmlResults) {
    // Save raw XML
    $wptResultsDir = dirname(__FILE__) . "/wpt_results/";
    $pathName      = $wptResultsDir . $id;
    if(!is_dir($pathName)) {
        mkdir($pathName, 0755, true);
    }
    $fh = fopen($pathName . "/xmlResult.xml", "w+");
    if(fwrite($fh, $xmlResults) === false) {
        // TODO: handle error
    }
    fclose($fh);
}

/**
 * Fetch all result assets and save locally
 *
 * @param  $id 'The wpt result ID'
 * @param  $xmlResult 'The contents of the result'
 * @return void
 *
 */
function saveWPTAssets($id, $xmlResult) {

    // Fetch individual assets
    $xml = new SimpleXMLElement( $xmlResult );

    $item = $xml->data->run->firstView->thumbnails;
    saveWPTAssetsChildrenOf($id, $item);

    $item = $xml->data->run->firstView->images;
    saveWPTAssetsChildrenOf($id, $item);

    $item = $xml->data->run->firstView->rawData;
    saveWPTAssetsChildrenOf($id, $item);

    $item = $xml->data->run->firstView->videoFrames;
    if($item) {
        saveWPTVideoFrames($id, $item);
    }

}

/**
 * Save the video frames
 *
 * @param  $id
 * @param  $videoFramesNode
 * @return void
 */
function saveWPTVideoFrames($id, $videoFramesNode) {
    global $wptResultsDir;
    $pathName = $wptResultsDir . $id;

    if(!is_dir($pathName)) {
        mkdir($pathName, 0, true);
    }

    foreach ($videoFramesNode->frame as $frame) {
        $image    = $frame->image;
        $item     = file_get_contents($image);
        $fileName = basename($image);
        $fh       = fopen($pathName . "/" . $fileName, "w+");
        if(fwrite($fh, $item) === false) {
            // TODO: handle error
        }
        fclose($fh);

    }
}

function saveWPTAssetsChildrenOf($id, $node) {
    global $wptResultsDir;
    $pathName = $wptResultsDir . $id;

    $dc = $node->children();
    if(!is_dir($pathName)) {
        mkdir($pathName, 0, true);
    }

    foreach ($dc as $child) {
        $fileName = basename($child);
        $item     = file_get_contents($child);
        $fh       = fopen($pathName . "/" . $fileName, "w+");
        if(fwrite($fh, $item) === false) {
            // TODO: handle error
        }
        fclose($fh);
    }
}

/**
 * Submit a request to the wpt host
 *
 * @param  $wptHost
 * @param  $urlToTest
 * @param  $label
 * @param  $remoteAgentId
 * @param  $runs
 * @param  $fvonly
 * @param  $video
 * @param  $navscript
 * @param  $authenticate
 * @param  $authuser
 * @param  $authpassword
 * @return string
 */
function submitWPTRequest($wptHost,
                          $urlToTest,
                          $label,
                          $remoteAgentId,
                          $runs,
                          $fvonly,
                          $video,
                          $navscript,
                          $authenticate,
                          $authuser,
                          $authpassword,
                          $format = "xml",
                          $private = 1,
                          $bwDown = null,
                          $bwUp = null,
                          $latency = null,
                          $plr = null,
                          $priority = 9) {
    $resultStatus               = array();
    $resultStatus['statusText'] = "Success";
    $baseURL                    = getBaseURL();
    // Defaults to -1 indicating failure
    $wptResultId        = "-1";
    $req                = new HttpRequest( $wptHost, HTTP_METH_POST );
    $params             = array();
    $params['agent']    = 'wptmonitor';
    $params['callback'] = $baseURL . '/wptCallBack.php';
    $params['url']      = $urlToTest;
    $params['f']        = $format;
    $params['label']    = $label;
    $params['runs']     = $runs;
    $params['fvonly']   = $fvonly;
    $params['video']    = $video;
    $params['private']  = $private;
    $params['priority'] = $priority;
    if($bwDown != null && $bwUp != null && $latency != null && $plr != null) {
        $params['location'] .= $remoteAgentId . '.custom';
        $params['bwDown']  = $bwDown;
        $params['bwUp']    = $bwUp;
        $params['latency'] = $latency;
        $params['plr']     = $plr;
    }else {
        $params['location'] = $remoteAgentId;
    }

    if($authenticate) {
        $params['authType'] = 0;
        $params['login']    = $authuser;
        $params['password'] = $authpassword;
    }
    if($navscript) {
        $params['script'] = $navscript;
    }
    $req->addQueryData($params);

    try {
        $response = $req->send();
        /**
         *
         * <?xml version="1.0" encoding="UTF-8"?>
         * <response>
         * <statusCode>400</statusCode>
         * <statusText>WPT Monitor is not intended to be used against this instance of WebPagetest.</statusText>
         * </response>
         *
         *
         * <?xml version="1.0" encoding="UTF-8"?>
         * <response>
         * <statusCode>400</statusCode>
         * <statusText>Invalid Script.  Please contact us if you need help with your test script.</statusText>
         * </response>
         *
         *
         * <?xml version="1.0" encoding="UTF-8"?>
         * <response>
         * <statusCode>200</statusCode>
         * <statusText>Ok</statusText>
         * <data>
         * <testId>101014_3A3</testId>
         * <xmlUrl>http://wpt.mtvly.com/xmlResult/101014_3A3/</xmlUrl>
         * <userUrl>http://wpt.mtvly.com/result/101014_3A3/</userUrl>
         * </data>
         * </response>
         */
        $responseCode = $req->getResponseCode();
        $responseXml  = $req->getResponseBody();

        if($responseCode != 200) {
            $resultStatus['statusText'] = "Job Submission failed with http response code: " . $responseCode;
            logOutput("[ERROR] [submitWPTRequest] Job Submission failed with http response code: " . $responseCode);
        }else {
            $xml = new SimpleXMLElement( $responseXml );
            if($xml->statusCode == "200") {
                $wptResultId = $xml->data->testId;
            }else {
                $resultStatus['statusText'] = "Job Submission failed with message: " . $xml->statusText;
                logOutput("[ERROR] [submitWPTRequest] Job Submission failed with message: " . $xml->statusText);
            }
        }
    }catch (HttpException $ex) {
        $resultStatus['statusText'] = "Exception: " . $ex->getMessage();
        logOutput("[ERROR] [submitWPTRequest] Exception: " . $ex->getMessage() . " " . $responseXml);
    }
    $resultStatus['wptResultId'] = $wptResultId;

    return $resultStatus;
}

/**
 * Process script tokens
 *
 * @param  $script
 * @return array
 */
function processTokens($script) {
    $result = array();
    $exp    = explode("\n", $script);

    foreach ($exp as $l) {
        $eqLocation = strpos($l, '=');
        $key        = trim(substr($l, 0, $eqLocation));
        $token      = trim(substr($l, $eqLocation+1));

        if(substr($token, 0, 11) == 'RANDOM_TEXT') {
            $startParen   = strpos($token, "(");
            $endParen     = strrpos($token, ")");
            $paramStr     = substr($token, $startParen+1, $endParen-$startParen-1);
            $result[$key] = generateTextFromList($paramStr);
            continue;
        }
        if(substr($token, 0, 13) == 'RANDOM_NUMBER') {
            $startParen   = strpos($token, "(");
            $endParen     = strrpos($token, ")");
            $paramStr     = substr($token, $startParen+1, $endParen-$startParen-1);
            $params       = explode(",", $paramStr);
            $result[$key] = generateRandomNumber($params[0], $params[1]);
            continue;
        }
        if(substr($token, 0, 20) == 'RANDOM_FUTURE_MONTH(') {
            $startParen   = strpos($token, "(");
            $endParen     = strrpos($token, ")");
            $paramStr     = substr($token, $startParen+1, $endParen-$startParen-1);
            $params       = explode(",", $paramStr);
            $result[$key] = generateRandomFutureMonth($params[0], $params[1]);
            continue;
        }
        if(substr($token, 0, 19) == 'RANDOM_FUTURE_DATE(') {
            $startParen   = strpos($token, "(");
            $endParen     = strrpos($token, ")");
            $paramStr     = substr($token, $startParen+1, $endParen-$startParen-1);
            $params       = explode(",", $paramStr);
            $result[$key] = generateRandomDate($params[0], $params[1], $params[2]);
            continue;
        }
    }

    return $result;
}

/**
 * Returns a random selection from a list of text items
 *
 * @param  $list
 * @return
 */
function generateTextFromList($list) {
    $l    = explode("`", $list);
    $size = sizeof($l);
    $num  = rand(0, (int)$size-1);

    return $l[$num];
}

/**
 * Generates a random number
 *
 * @param int $from
 * @param int $to
 * @return int
 */
function generateRandomNumber($from = 0, $to = 10000) {
    $num = rand((int)$from, (int)$to);

    return $num;
}

/**
 * Generate a random month in the future
 *
 * @param int $min Minumum number of months from now
 * @param int $max maximum number of months from now
 * @return int
 */
function generateRandomFutureMonth($min, $max) {
    $month = (int)Date('n');
    $r     = rand((int)$min, (int)$max);

    return $month+$r;
}

/**
 * Generate a random date with the given format
 *
 * @param int $from
 * @param int $to
 * @param string $format
 * @return string
 */
function generateRandomDate($from = 0, $to = 365, $format = "Y-m-d") {
    // Get rid of any ' in format
    $format = str_replace("'", "", $format);
    $r      = rand((int)$from, (int)$to);
    $time   = time()+( $r*86400 );
    $date   = Date($format, $time);

    return $date;
}

function getWPTResultPath($wptResultId) {
  $path = "/" . substr($wptResultId, 0, 2) . "/" . substr($wptResultId, 2, 2) . "/" . substr($wptResultId, 4, 2) . "/" . substr($wptResultId, 7) . "/";
  return $path;
}

/**
 * Check to see if new user registration is allowed
 * @return boolean
 */
function isRegistrationEnabled() {
    $configTable = Doctrine_Core::getTable('WPTMonitorConfig');
    $config      = $configTable->find(1);

    return $config['EnableRegistration'];
}

function getWptConfigFor($key) {
    $configTable = Doctrine_Core::getTable('WPTMonitorConfig');
    $config      = $configTable->find(1);

    return $config['JobProcessorAuthenticationKey'];
}

function getWptLocations() {
    $q = Doctrine_Query::create()
                       ->from('WPTLocation l');
    $q->orderBy('location');
    $locations = $q->execute();
    $q->free(true);

    return $locations;
}

function processJobsForUser($userId, $jobId = null, $force = false, $runLabel = "", $priority = 9) {
    global $wptRuntest;
    // Update location information
    $locationInformation = getLocationInformation();

    logOutput("[DEBUG] [processJobsForUsers] start: " . timestamp() . " mem: " . intval(memory_get_usage()/1024));

    $userTable = Doctrine_Core::getTable('User');
    $user      = $userTable->find($userId);

    $scriptTable = Doctrine_Core::getTable('WPTScript');
    $jobTable    = Doctrine_Core::getTable('WPTJob');
    if(!$user) {
        logOutput("[ERROR] [ProcessJob] User not found for ID: " . $userId);

        return;
    }
    logOutput("[INFO] [ProcessJob] Processing jobs for " . $user['Username']);

    if($jobId != null) {
        $q = Doctrine_Query::create()
                           ->from('WPTJob j')
                           ->where('j.Id= ?', $jobId)
                           ->andWhere('j.UserId = ?', $userId)
                           ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
        $result = $q->fetchArray();
    }else {
        $q = Doctrine_Query::create()
                           ->from('WPTJob j')
                           ->where('j.UserId = ?', $userId)
                           ->andWhere('active = 1')
                           ->andWhere('(lastrun is NULL or lastrun + frequency*60 < ?)', time())
                           ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY)
                           ->orderBy('lastrun');
        $result = $q->fetchArray();
    }

    logOutput("[DEBUG] [processJobsForUsers] before looping through results: " . timestamp() . " mem: " . intval(memory_get_usage()/1024));
    try {
        foreach ($result as $row) {
            // Check current queue size for the jobs configured location
            // If it's greater than the threshold then skip the job submission.
            $skipJobSubmission = false;
            $locationFound     = false;
            foreach ($locationInformation as $location) {
                if($location['hostURL'] == $row['Host'] && $location['id'] == $row['Location']) {
                    $locationFound = true;
                    $queueSize     = $location['PendingTests'];
                    $threshold     = getLocationThreshold($location['hostURL'], $location['id']);

                    if($threshold != 0 && ( $queueSize && $threshold && ( $queueSize>$threshold ) )) {
                        logOutput("[INFO] [ProcessJob] Skipping job due to high load on location: " . $location['hostURL'] . " " . $location['id'] . " queue size: " . $queueSize . " greater than the threshold: " . $threshold);
                        $skipJobSubmission = true;
                        break;
                    }
                }
            }
            if($skipJobSubmission) {
                continue;
            }

            if(!$locationFound AND $row['Active'] != 0) {
                logOutput("[INFO] [ProcessJob] Skipping job due to lack of location: " . $row['Location'] . " jobID: " . $row['Id']);
                continue;
            }

            $id     = $row['Id'];
            $label  = $row['Label'];
            $dte    = current_seconds();
            $script = $scriptTable->find($row['WPTScriptId']);

            // Data stored in the table is in minutes, convert it to seconds.
            $frequencyInSeconds = $row['Frequency']*60;

            if($row['Active'] == 0 && !$force) {
                logOutput("[INFO] [ProcessJob] Skipped Inactive job. Id:$id Label:$label");
            }else {
                // If the field is empty or time is up it's time to run
                if($force || ( ( !$row['Lastrun'] || $dte>$row['Lastrun']+$frequencyInSeconds ) )) {
                    // Process script if it exists
                    if($script['URLScript']) {
                        $url = processURLScript($script['URLScript'], $script['URL']);
                        logOutput("[INFO] [ProcessJob] Id:$id Label:$label URL:$url");
                    }else {
                        $url = $script['URL'];
                    }
                    if($script['NavigationScript']) {
                        $navscript = processURLScript($script['URLScript'], $script['NavigationScript']);
                    }else {
                        $navscript = $script['NavigationScript'];
                    }
                    $authenticate          = $script['Authenticate'];
                    $authuser              = $script['AuthUser'];
                    $authpassword          = $script['AuthPassword'];
                    $wptResult             = new WPTResult();
                    $wptResult['WPTJobId'] = $row['Id'];
                    $wptResult['Date']     = current_seconds();
                    $wptResult['RunLabel'] = $runLabel;
                    // TODO: Add option in JOB Table for inclusion of tracking param
//          if ( true ){
//            $mjid=$row['Id'];
//            if ( strpos($url,"?") > -1 ){
//              $url = $url.'&__wptmjid='.$mjid.'_'.$wptResult['Date'];
//            } else {
//              $url = $url.'?__wptmjid='.$mjid.'_'.$wptResult['Date'];
//            }
//          }
                    // Default DSL settings

                    $private = 1;
                    $bwDown  = null;
                    $bwUp    = null;
                    $latency = null;
                    $plr     = null;

                    if(is_numeric($row['WPTBandwidthDown'])) {
                        $bwDown = $row['WPTBandwidthDown'];
                    }

                    if(is_numeric($row['WPTBandwidthUp'])) {
                        $bwUp = $row['WPTBandwidthUp'];
                    }

                    if(is_numeric($row['WPTBandwidthLatency'])) {
                        $latency = $row['WPTBandwidthLatency'];
                    }

                    if(is_numeric($row['WPTBandwidthPacketLoss'])) {
                        $plr = $row['WPTBandwidthPacketLoss'];
                    }

                    $format = "xml";

                    logOutput("[DEBUG] [processJobsForUsers] before submitWPTRequest: " . timestamp() . " mem: " . intval(memory_get_usage()/1024));

                    $wptRes = submitWPTRequest(
                        $row['Host'] . $wptRuntest,
                        $url,
                        $row['Label'],
                        $row['Location'],
                        $row['Runs'],
                        $row['FirstViewOnly'],
                        $row['Video'],
                        $navscript,
                        $authenticate,
                        $authuser,
                        $authpassword,
                        $format,
                        $private,
                        $bwDown,
                        $bwUp,
                        $latency,
                        $plr,
                        $priority
                    );

                    $wptResultId   = $wptRes['wptResultId'];
                    $wptResultText = $wptRes['statusText'];
                    logOutput("[DEBUG] [processJobsForUsers] after submitWPTRequest: " . timestamp() . " mem: " . intval(memory_get_usage()/1024));
//          $row['Lastrun'] = $dte;
                    if($wptResultId == "-1") {
                        $wptResult['Status']          = "905";
                        $wptResult['ValidationState'] = 2;
                    }else {
                        $wptResult['Status'] = "100";
                    }
                    $wptResult['WPTResultId']            = $wptResultId;
                    $wptResult['WPTHost']                = $row['Host'];
                    $wptResult['DownloadResultXml']      = $row['DownloadResultXml'];
                    $wptResult['DownloadDetails']        = $row['DownloadDetails'];
                    $wptResult['MaxDownloadAttempts']    = $row['MaxDownloadAttempts'];
                    $wptResult['Runs']                   = $row['Runs'];
                    $wptResult['RunToUseForAverage']     = $row['RunToUseForAverage'];
                    $wptResult['FirstStatusUpdate']      = current_seconds();
                    $wptResult['LastStatusUpdate']       = current_seconds();
                    $wptResult['MultiStep']              = $script['MultiStep'];
                    $wptResult['Validate']               = $script['Validate'];
                    $wptResult['ValidationRequest']      = $script['ValidationRequest'];
                    $wptResult['ValidationType']         = $script['ValidationType'];
                    $wptResult['ValidationMarkAs']       = $script['ValidationMarkAs'];
                    $wptResult['ValidationMarkAsElse']   = $script['ValidationMarkAsElse'];
                    $wptResult['WPTBandwidthDown']       = $bwDown;
                    $wptResult['WPTBandwidthUp']         = $bwUp;
                    $wptResult['WPTBandwidthLatency']    = $latency;
                    $wptResult['WPTBandwidthPacketLoss'] = $plr;

                    $wptResult->save();
                    $r            = $jobTable->find($id);
                    $r['Lastrun'] = $dte;
                    $r->save();
                    unset( $row );
                    unset( $r );
                    unset( $wptResult );
                    logOutput("[INFO] [ProcessJob] Id:$id Label:$label Sumitted job for processing\n url: " . $url);
                    if($navscript) {
                        logOutput("[INFO] [ProcessJob] Id:$id Label:$label Nav Script\n" . $navscript);
                    }
                }else {
                    logOutput("[INFO] [ProcessJob] Skipping Job. Scheduled interval not passed. Id:$id Label:$label ");
                }
            }
        }
    }catch (Exception $e) {
        error_log("[WPTMonitor] Failed while Executing job: " . $id . " message: " . $e->getMessage());
        logOutput('[ERROR] [processJobsForUsers] Exception : ' . $e->getMessage() . "");
        exit;
    }
    logOutput("[DEBUG] [processJobsForUsers] after looping: " . timestamp() . " mem: " . intval(memory_get_usage()/1024));
    unset( $result );
    $user->free(true);
    unset( $user );
    logOutput("[DEBUG] [processJobsForUsers] after cleanup: " . timestamp() . " mem: " . intval(memory_get_usage()/1024));
}

/**
 * @param null $seconds
 * @param string $format
 * @return string
 */
function timestamp($seconds = null, $format = "Y-m-d H:i:s") {
    $now = new DateTime();
    if($seconds != null) {
        $now->setTimestamp($seconds);
    }

    return $now->format($format);
}

// return our current unix time in millis
function current_seconds() {
//  date_default_timezone_set('UTC');
    $t = gmdate('U');

//  date_default_timezone_set($_SESSION['ls_timezone']);
    return $t;

    list( $usec, $sec ) = explode(" ", microtime());

    return round(( (float)$usec+(float)$sec ));
}

function processResultsForAll($resultId = null) {
    global $wptXmlResult;
    // Process results

    try {
        if($resultId == null) {
            // Only fetch jobs that are "100" / still processing
            $q       = Doctrine_Query::create()
                                     ->from('WPTResult r')
                                     ->where('r.Status = ?', '100');
            $results = $q->execute();
        }else {
            $q       = Doctrine_Query::create()
                                     ->from('WPTResult r')
                                     ->where('r.WPTResultId = ?', $resultId);
            $results = $q->execute();
        }
        $q->free(true);
        foreach ($results as $result) {
            $wptResultId = $result['WPTResultId'];
            $runToUse    = $result['RunToUseForAverage'];

            // If we have no valid wpt result id then skip and continue
            if($wptResultId<0) {
                continue;
            }

            $id = $result['Id'];

            // If we have a resultId we assume we are forcing a refresh and will ignore max attempts
            if(!$resultId) {
                if($result['DownloadAttempts']>$result['MaxDownloadAttempts']) {
                    // Set to 900 to indicate timeout
                    logOutput("[WARN] [ProcessResults] Max download attempts exceeded. Deactivating WPTResultId: $wptResultId Id: $id");
                    $result['Status'] = "910";
                    $result->save();
                    continue;
                }
                // Increment download attempt count;
                incrementDownloadAttempts($id);
            }
            if(!$wptResultId) {
                continue;
            }
            try {
                $resultXml = file_get_contents($result['WPTHost'] . $wptXmlResult . $wptResultId . "/");
            }catch (Exception $e) {
                logOutput("[ERROR] [ProcessResults] Failed to retrieve xml for WPTResultId: $wptResultId Id: $id");
                continue;
            }

            if(!$resultXml) {
                logOutput("[ERROR] [ProcessResults] Empty xml retrieved for WPTResultId: $wptResultId Id: $id");
                continue;
            }

            try {
                $xml = new SimpleXMLElement( $resultXml );
            }catch (Exception $e) {
                // Ignore and continue
                logOutput("[ERROR] [ProcessResults] Failed to parse XML for WPTResultId: $wptResultId Id: $id");
                continue;
            }
            $status = $xml->statusCode;
            // If no valid statusCode then assume error.
            if(!$xml || !$xml->statusCode) {
                logOutput("[ERROR] [ProcessResults] Failed to get statusCode for WPTResultId: $wptResultId Id: $id");
                continue;
            }
            // If 100 then it's still waiting
            $result['Status'] = $status;

            if($status == "100") {
                logOutput("[INFO] [ProcessResults] $wptResultId Processing");
                continue;
            }
            // Only process 200s
            if($status == "200" || $resultId) {
                logOutput("[INFO] [ProcessResults] $wptResultId Completed");
                // Check for empty run
                $headersLocation = $xml->data->run[$runToUse-1]->firstView->rawData->headers;
                // TODO: Remove Strangeloop specific code, or make this feature generic.
                if($headersLocation) {
                    $headers = file_get_contents($headersLocation);
                }
                $request57 = strpos($headers, "Request 6:");

                $pageDataLocation = $xml->data->run[0]->firstView->rawData->pageData;
                if($pageDataLocation) {
                    $pageData = file_get_contents($pageDataLocation);
                }
                $pageDataLocationRepeatView = $xml->data->run[0]->repeatView->rawData->pageData;
                if($pageDataLocationRepeatView) {
                    $pageDataRepeatView = file_get_contents($pageDataLocationRepeatView);
                }

                if($pageData) {
                    $lines = explode("\n", $pageData);
                    if($lines) {
                        $headers = explode("\t", $lines[0]);
                        $line    = explode("\t", $lines[1]);
                    }
                    foreach ($headers as $key => $header) {
                        //Set default
                        $dialerId = '0';
                        if($header == "Dialer ID") {
                            $dialerId = $line[$key];
                        }
                        if($header == "Error Code") {
                            // Ignore the 99999.
                            //            if (!$line[$key] == "99999"){
                            $errorCode = $line[$key];
                            //            }
                        }
                    }
                }
                if($errorCode) {
                    $status = $errorCode;
                }else {
                    $status = $xml->statusCode;
                }
                // If 100 then it's still waiting
                $result['Status'] = $status;

                if($xml->data->runs == 0) {
                    logOutput("[WARN] [ProcessResults] Result set with 0 runs for WPTResultdId: $wptResultId Id: $id");
                    updateJobStatus($id, "999");
                    continue;
                }
                $testComplete = $xml->data->run->firstView->results->date;
                // if = then use average.
                $avgRun = $xml->data->average->firstView->avgRun;

                if($runToUse == 0) {
                    $avgFV                         = $xml->data->average->firstView;
                    $avgRV                         = $xml->data->average->repeatView;
                    $requestDataLocation           = $xml->data->run[$avgRun-1]->firstView->rawData->requestsData;
                    $requestDataLocationRepeatView = $xml->data->run[$avgRun-1]->repeatView->rawData->requestsData;
                    $pageDataLocation              = $xml->data->run[$avgRun-1]->firstView->rawData->pageData;
                }else {
                    $avgFV                         = $xml->data->run[$runToUse-1]->firstView->results;
                    $avgRV                         = $xml->data->run[$runToUse-1]->repeatView->results;
                    $requestDataLocation           = $xml->data->run[$runToUse-1]->firstView->rawData->requestsData;
                    $requestDataLocationRepeatView = $xml->data->run[$runToUse-1]->repeatView->rawData->requestsData;
                    $pageDataLocationRepeatView    = $xml->data->run[$avgRun-1]->repeatView->rawData->pageData;
                }
                $result['DialerId'] = $dialerId;
                if($avgFV) {
                    $result['AvgFirstViewLoadTime']            = $avgFV->loadTime;
                    $result['AvgFirstViewFirstByte']           = $avgFV->TTFB;
                    $result['AvgFirstViewStartRender']         = $avgFV->render;
                    $result['AvgFirstViewDocCompleteTime']     = $avgFV->docTime;
                    $result['AvgFirstViewDocCompleteRequests'] = $avgFV->requestsDoc;
                    $result['AvgFirstViewDocCompleteBytesIn']  = $avgFV->bytesInDoc;
                    $result['AvgFirstViewDomTime']             = $avgFV->domContentLoadedEventStart;
                    $result['AvgFirstViewFullyLoadedTime']     = $avgFV->fullyLoaded;
                    $result['AvgFirstViewFullyLoadedRequests'] = $avgFV->requests;
                    $result['AvgFirstViewFullyLoadedBytesIn']  = $avgFV->bytesIn;
                }
                if($avgRV) {
                    $result['AvgRepeatViewLoadTime']            = $avgRV->loadTime;
                    $result['AvgRepeatViewFirstByte']           = $avgRV->TTFB;
                    $result['AvgRepeatViewStartRender']         = $avgRV->render;
                    $result['AvgRepeatViewDocCompleteTime']     = $avgRV->docTime;
                    $result['AvgRepeatViewDocCompleteRequests'] = $avgRV->requestsDoc;
                    $result['AvgRepeatViewDocCompleteBytesIn']  = $avgRV->bytesInDoc;
                    $result['AvgRepeatViewDomTime']             = $avgRV->domContentLoadedEventStart;
                    $result['AvgRepeatViewFullyLoadedTime']     = $avgRV->fullyLoaded;
                    $result['AvgRepeatViewFullyLoadedRequests'] = $avgRV->requests;
                    $result['AvgRepeatViewFullyLoadedBytesIn']  = $avgRV->bytesIn;
                }


                if($testComplete) {
                    $result['Date'] = $testComplete;
                }else {
                    $result['Date'] = current_seconds();
                }
                if($result['DownloadResultXml']) {
                    saveWPTXmlResults($wptResultId, $resultXml);
                }
                if($result['DownloadDetails']) {
                    saveWPTAssets($wptResultId, $resultXml);
                }
                if($result['Validate']) {
                    $validationRequest    = $result['ValidationRequest'];
                    $validationType       = $result['ValidationType'];
                    $validationMarkAs     = $result['ValidationMarkAs'];
                    $validationMarkAsElse = $result['ValidationMarkAsElse'];
                    $requestData          = retrieveRequestsData($requestDataLocation);
                    if($requestDataLocationRepeatView) {
                        $requestDataRepeatView = retrieveRequestsData($requestDataLocationRepeatView);
                    }
                    $requestDataArray[]        = $requestData;
                    $requestDataArray[]        = $requestDataRepeatView;
                    $result['ValidationState'] = $validationMarkAsElse;
                    foreach ($requestDataArray as $reqData) {
                        if(is_array($reqData)) {
                            foreach ($reqData as $request) {
                                if(preg_match($validationRequest, $request['url'])) {
                                    if($validationType == VALIDATION_TYPE_MATCH) {
                                        $result['ValidationState'] = $validationMarkAs;
                                        break;
                                    }else {
                                        if($validationType == VALIDATION_TYPE_NOMATCH) {
                                            break;
                                        }else {
                                            $result['ValidationState'] = $validationMarkAs;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if($result['AvgFirstViewLoadTime'] = 0
                    && $result['AvgFirstViewFirstByte'] = 0
                        && $result['AvgFirstViewDocCompleteTime'] = 0
                            && $result['AvgFirstViewStartRender'] = 0
                ) {
                    $result['ValidationState'] = 2;
                }
                // If this is a multi page job page test will report incorrect numbers. This routine
                // Will calculate correct numbers from the actual request data.
                // Sums up the total for each element across all pages.
                if($result['MultiStep']) {
                    $pageData = file_get_contents($pageDataLocation);
                    if($pageData) {
                        $pageDataArray = delimitedToArray($pageData, "\t", true);
                        if($pageDataArray) {
                            $result['AvgFirstViewDocCompleteTime']     = array_sum(getArrayFor($pageDataArray,
                                                                                               'Doc Complete Time (ms)'));
                            $result['AvgFirstViewDocCompleteRequests'] = array_sum(getArrayFor($pageDataArray,
                                                                                               'Requests (Doc)'));
                            $result['AvgFirstViewDocCompleteBytesIn']  = array_sum(getArrayFor($pageDataArray,
                                                                                               'Bytes In (Doc)'));
                            $result['AvgFirstViewFullyLoadedTime']     = array_sum(getArrayFor($pageDataArray,
                                                                                               'Activity Time(ms)'));
                            $result['AvgFirstViewFullyLoadedRequests'] = array_sum(getArrayFor($pageDataArray,
                                                                                               'Requests'));
                            $result['AvgFirstViewFullyLoadedBytesIn']  = array_sum(getArrayFor($pageDataArray,
                                                                                               'Bytes In'));
                            $result['AvgFirstViewFirstByte']           = array_sum(getArrayFor($pageDataArray,
                                                                                               'Time to First Byte (ms)'));
                            $result['AvgFirstViewStartRender']         = array_sum(getArrayFor($pageDataArray,
                                                                                               'Time to Start Render (ms)'));
                        }
                    }
                    $pageDataRepeatView = file_get_contents($pageDataLocationRepeatView);
                    if($pageDataRepeatView) {
                        $pageDataArrayRepeatView = delimitedToArray($pageDataRepeatView, "\t", true);
                        if($pageDataArrayRepeatView) {
                            $result['AvgRepeatViewDocCompleteTime']     = array_sum(getArrayFor($pageDataArrayRepeatView,
                                                                                                'Doc Complete Time (ms)'));
                            $result['AvgRepeatViewDocCompleteRequests'] = array_sum(getArrayFor($pageDataArrayRepeatView,
                                                                                                'Requests (Doc)'));
                            $result['AvgRepeatViewDocCompleteBytesIn']  = array_sum(getArrayFor($pageDataArrayRepeatView,
                                                                                                'Bytes In (Doc)'));
                            $result['AvgRepeatViewFullyLoadedTime']     = array_sum(getArrayFor($pageDataArrayRepeatView,
                                                                                                'Activity Time(ms)'));
                            $result['AvgRepeatViewFullyLoadedRequests'] = array_sum(getArrayFor($pageDataArrayRepeatView,
                                                                                                'Requests'));
                            $result['AvgRepeatViewFullyLoadedBytesIn']  = array_sum(getArrayFor($pageDataArrayRepeatView,
                                                                                                'Bytes In'));
                            $result['AvgRepeatViewFirstByte']           = array_sum(getArrayFor($pageDataArray,
                                                                                                'Time to Repeat Byte (ms)'));
                            $result['AvgRepeatViewStartRender']         = array_sum(getArrayFor($pageDataArray,
                                                                                                'Time to Start Render (ms)'));
                        }
                    }
                }
                $result->save();
                updateJobstatus($id, $status);
            }
        }
    }catch (Exception $e) {
        error_log("[WPTMonitor] Failed while Listing jobs: " . $wptResultId . " message: " . $e->getMessage());
        logOutput('[ERROR] [processResultsForAll] Exception : ' . $e->getMessage() . "");
    }
    unset( $result );
}

function retrieveRequestsData($location) {
    $requests = array();
    try {
        $requestsData = file_get_contents($location);
        if($requestsData) {
            $lines = explode("\n", $requestsData);
            if($lines) {
                $headers = explode("\t", $lines[0]);
            }
            foreach ($headers as $key => $header) {
                if($header == "Host") {
                    $hostKey = $key;
                }
                if($header == "URL") {
                    $urlKey = $key;
                }
                if($header == "Real Start Time (ms)") {
                    $realStartTimeKey = $key;
                }
                if($header == "Full Time to Load (ms)") {
                    $fullTime = $key;
                }
            }
            foreach ($lines as $key => $line) {
                $request = array();

                if($key == 0) {
                    continue;
                }
                $l                        = explode("\t", $line);
                $request['url']           = $l[$hostKey] . $l[$urlKey];
                $request['realStartTime'] = $l[$realStartTimeKey];
                $request['fullTime']      = $l[$fullTime];
                $requests[]               = $request;
            }
        }else {
            error_log("Failed to fetch file " . $location);
        }
    }catch (Exception $e) {
        logOutput("[WARN] [retrieveRequestsData] Failed to retrieve requests data " . $e->getMessage());
    }

    return $requests;
}

/**
 * Process the URL script
 *
 * @param  $script
 * @param  $url
 * @return mixed
 */
function processURLScript($script, $url) {
    try {
        $tokenValues = processTokens($script);
        foreach ($tokenValues as $tokenKey => $tokenValue) {
            $url = str_replace($tokenKey, $tokenValue, $url);
        }
    }catch (Exception $e) {
        logOutput("[ERROR] [processURLScript] " . $e->getMessage());
    }

    return $url;
}

function updateJobStatus($id, $status) {
    $q = Doctrine_Query::create()
                       ->from('WPTResult r')
                       ->where('r.Id = ?', $id);
    if($q->count()>0) {
        $result = $q->fetchOne();
        $q->free(true);
        $result['Status'] = $status;
        $result->save();
    }
}

function incrementDownloadAttempts($id) {
    try {
        $q = Doctrine_Query::create()
                           ->from('WPTResult r')
                           ->where('r.Id = ?', $id);
        if($q->count()>0) {
            $result = $q->fetchOne();
            $q->free(true);
            if(!$result['FirstStatusUpdate']) {
                $result['FirstStatusUpdate'] = current_seconds();
            }
            $result['DownloadAttempts'] = $result['DownloadAttempts']+1;
            $result['LastStatusUpdate'] = current_seconds();
            $result->save();
        }
    }catch (Exception $ex) {
        print $ex;
    }
}

function getReportDataForLabel($label, $fromDate, $toDate, $stddev, $runLabel = null) {
    $q   = Doctrine_Query::create()
                         ->from('WPTJob j')
                         ->where('j.Label = ?', $label)
                         ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
    $job = $q->fetchOne();
    $q->free(true);
    $id = $job['Id'];
    if($runLabel) {
        $q = Doctrine_Query::create()
                           ->from('WPTResult r')
                           ->where('r.WPTJobId= ?', $id)
                           ->andWhere('r.RunLabel = ?', $runLabel)
                           ->andWhere('r.Date >= ?', $fromDate)
                           ->andWhere('r.Date <= ?', $toDate)
                           ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
    }else {
        $q = Doctrine_Query::create()
                           ->from('WPTResult r')
                           ->where('r.WPTJobId= ?', $id)
                           ->andWhere('r.Date >= ?', $fromDate)
                           ->andWhere('r.Date <= ?', $toDate)
                           ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
    }
    $results = $q->fetchArray();
    $q->free(true);

    return $results;
}

function getReportResultsForLabel($label, $fromDate, $toDate, $stddev, $runLabel = null, $ninetieth = null) {
    $sum = array();
    $q   = Doctrine_Query::create()
                         ->from('WPTJob j')
                         ->where('j.Label = ?', $label)
                         ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
    $job = $q->fetchOne();
    $q->free(true);
    $id = $job['Id'];

    if($runLabel) {
        $q = Doctrine_Query::create()
                           ->from('WPTResult r')
                           ->where('r.WPTJobId= ?', $id)
                           ->andWhere('r.ValidationState > 1')
                           ->andWhere('r.AvgFirstViewFirstByte != ""')
                           ->andWhere('r.AvgFirstViewDocCompleteTime != ""')
                           ->andWhere('r.Status = 200')
                           ->orWhere('r.Status = 99999')
                           ->andWhere('r.Date >= ?', $fromDate)
                           ->andWhere('r.Date <= ?', $toDate)
                           ->andWhere('r.RunLabel = ?', $runLabel)
                           ->orderby('r.AvgFirstViewDocCompleteTime')
                           ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
        if($ninetieth) {
            $limit = $q->count()*.9;
            $q     = Doctrine_Query::create()
                                   ->from('WPTResult r')
                                   ->where('r.WPTJobId= ?', $id)
                                   ->andWhere('r.ValidationState > 1')
                                   ->andWhere('r.AvgFirstViewFirstByte != ""')
                                   ->andWhere('r.AvgFirstViewDocCompleteTime != ""')
                                   ->andWhere('r.Status = 200')
                                   ->orWhere('r.Status = 99999')
                                   ->andWhere('r.Date >= ?', $fromDate)
                                   ->andWhere('r.Date <= ?', $toDate)
                                   ->andWhere('r.RunLabel = ?', $runLabel)
                                   ->orderby('r.AvgFirstViewDocCompleteTime')
                                   ->limit($limit)
                                   ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
        }
    }else {
        $q = Doctrine_Query::create()
                           ->from('WPTResult r')
                           ->where('r.WPTJobId= ?', $id)
                           ->andWhere('r.ValidationState < 2')
                           ->andWhere('r.AvgFirstViewFirstByte != ""')
                           ->andWhere('r.AvgFirstViewDocCompleteTime != ""')
                           ->andWhere('r.Status = 200 OR r.Status = 99999')
                           ->andWhere('r.Date >= ?', $fromDate)
                           ->andWhere('r.Date <= ?', $toDate)
                           ->orderBy('r.AvgFirstViewDocCompleteTime')
                           ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
        if($ninetieth) {
            $limit = $q->count()*.9;
            $q     = Doctrine_Query::create()
                                   ->from('WPTResult r')
                                   ->where('r.WPTJobId= ?', $id)
                                   ->andWhere('r.ValidationState < 2')
                                   ->andWhere('r.AvgFirstViewFirstByte != ""')
                                   ->andWhere('r.AvgFirstViewDocCompleteTime != ""')
                                   ->andWhere('r.Status = 200 OR r.Status = 99999')
                                   ->andWhere('r.Date >= ?', $fromDate)
                                   ->andWhere('r.Date <= ?', $toDate)
                                   ->orderBy('r.AvgFirstViewDocCompleteTime')
                                   ->limit($limit)
                                   ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
        }
    }
    $results = $q->fetchArray();
    $q->free(true);
    if(sizeof($results) == 0) {
        return;
    }
    $count = 0;

    foreach ($results as $result) {
        $sum['ttfb'] += $result['AvgFirstViewFirstByte'];
        $sum['render'] += $result['AvgFirstViewStartRender'];
        $sum['docTime'] += $result['AvgFirstViewDocCompleteTime'];
        $sum['domTime'] += $result['AvgFirstViewDocCompleteTime']-$result['AvgFirstViewDomTime'];
        $count++;
    }

    $avg['count'] = $count;
    if($count>0) {
        $avg['ttfb']    = number_format(( $sum['ttfb']/$count )/1000, 2);
        $avg['render']  = number_format(( $sum['render']/$count )/1000, 2);
        $avg['domTime'] = number_format($sum['domTime']/$count/1000, 2);
        $avg['docTime'] = number_format($sum['docTime']/$count/1000, 2);
    }
    if($stddev) {
        $avg['ttfb']    = number_format(standard_deviation(getArrayFor($results, 'AvgFirstViewFirstByte'))/1000, 2);
        $avg['render']  = number_format(standard_deviation(getArrayFor($results, 'AvgFirstViewStartRender'))/1000, 2);
        $avg['domTime'] = number_format(standard_deviation(getArrayFor($results, 'AvgFirstViewDomTime'))/1000, 2);
        $avg['docTime'] = number_format(standard_deviation(getArrayFor($results, 'AvgFirstViewDocCompleteTime'))/1000,
                                        2);
    }
    $avg['label'] = $job['Label'];
    $avg['jobId'] = $job['Id'];

    return $avg;
}

function getMaxJobsPerMonth($userId) {
    // Find max jobs allowed per month
    $userTable       = Doctrine_Core::getTable('User');
    $user            = $userTable->find($userId);
    $maxJobsPerMonth = $user['MaxJobsPerMonth'];

    return $maxJobsPerMonth;
}

function getUserJobCount($userId) {
    // TODO: Will need to update this when support for multi location jobs is added.
    $q               = Doctrine_Query::create()
                                     ->select('j.Frequency')
                                     ->from('WPTJob j')
                                     ->where('j.UserId = ?', $userId)
                                     ->andWhere('j.Active = ?', true)
                                     ->setHydrationMode(Doctrine_Core::HYDRATE_ARRAY);
    $jobs            = $q->fetchArray();
    $currentJobCount = 0;
    foreach ($jobs as $job) {
        $currentJobCount += ( 43200/$job['Frequency'] );
    }

    return $currentJobCount;
}

/*
 * Check the QueueGrowthCount and email an alert if it has grown for 4 consecutive times.
 */
function checkQueueGrowthCountAndEmailAlert() {
    $queueStatusFilename = './QueueStatus.ini';
    $queueStatus         = parse_ini_file($queueStatusFilename, true);
//  $message = "";
    $configTable             = Doctrine_Core::getTable('WPTMonitorConfig');
    $config                  = $configTable->find(1);
    $siteContactEmailAddress = $config['SiteContactEmailAddress'];
    foreach ($queueStatus as $key => $status) {
        if($status['QueueGrowthCount']>4) {
            logOutput('[INFO] [checkQueueGrowthCountAndEmailAlert] QueueGrowthCheck has grown over 4 times for: ' . $key);
            // Growth rate has exceeded threshold, but only send an alert once per hour.
            // Check time stamp
            if(!array_key_exists("LastQueueGrowthAlertTime",
                                 $status) || ( current_seconds()>$status["LastQueueGrowthAlertTime"]+3600 )
            ) {
                logOutput('[INFO] [checkQueueGrowthCountAndEmailAlert] QueueGrowthCheck for: ' . $key . ' has not alerted since: ' . $status["LastQueueGrowthAlertTime"]);
                $message = "\n--------------------------------------------\n";
                $message .= "Queue Growth alert for location: " . $status['id'] . "\n";
                $message .= "In Queue: " . $status['PendingTests'] . "\n";
                $message .= "Testers: " . $status['AgentCount'] . "\n";
                $message .= "Timestamp: " . timestamp() . "\n";
                $message .= "\n--------------------------------------------\n";
                $message .= "Queue count continues to grow. Please provision more testers or decrease the runrate for this location.";
                // Update alert time stamp
                $queueStatus[$key]['LastQueueGrowthAlertTime'] = current_seconds();
                writeIniFile($queueStatus, $queueStatusFilename, true);
            }else {
//        logOutput('[INFO] [checkQueueGrowthCountAndEmailAlert] QueueGrowthCheck for: '.$key.' alert interval has not passed. Last Alert: '.$status["LastQueueGrowthAlertTime"]);
            }
        }
    }
    if(isset( $message )) {
        sendEmailAlert($siteContactEmailAddress, $message);
        logOutput("[INFO] [checkQueueGrowthCountAndEmailAlert] Alert message sent:\n" . $message);
    }
}

/*
 * Checks the current count of jobs in queue compared to the previous run.
 * If the pending tests for the current info is greater than the last check 
 * A counter is incremented. 'QueueGrowthCount'
 */
function updateQueueProcessRate() {
    $queueStatusFilename = './QueueStatus.ini';
//  $updateFrequency = 600;
    $currentTime = current_seconds();
    try {
        // Write current queue status
        $locationInformation = getLocationInformation();

        // First time through, or file got deleted.
        if(!file_exists($queueStatusFilename)) {
            foreach ($locationInformation as $key => $location) {
                $locationInformation[$key]['QueueGrowthCount']          = '0';
                $locationInformation[$key]['QueueGrowthCheckTimeStamp'] = $currentTime;
            }
            writeIniFile($locationInformation, $queueStatusFilename, true);
        }else {
            // Compare previous status to current status to detect queue growth.
            $previousLocationInformation = parse_ini_file($queueStatusFilename, true);
            foreach ($previousLocationInformation as $key => $previousLocation) {
                $location = $locationInformation[$key];

                // Bring over previous setting for Last Alert Times.
                $location['LastQueueGrowthAlertTime'] = $previousLocation['LastQueueGrowthAlertTime'];
                $location['LastTesterRatioAlertTime'] = $previousLocation['LastTesterRatioAlertTime'];
                $location['QueueGrowthCount']         = $previousLocation['QueueGrowthCount'];

//        if ( $previousLocation['QueueGrowthCheckTimeStamp'] + $updateFrequency < $currentTime ){
                logOutput('[INFO] [updateQueueProcessRate] QueueGrowthCheck for : ' . $key);

                if($location['PendingTests'] == 0) {
                    $location['QueueGrowthCount'] = '0';
                    logOutput('[INFO] [updateQueueProcessRate] Resetting count QueueGrowthCheck for: ' . $key);
                }else {
                    if($location['PendingTests']>=$previousLocation['PendingTests']) {
                        $location['QueueGrowthCount'] = $previousLocation['QueueGrowthCount']+1;
                        logOutput('[INFO] [updateQueueProcessRate] Incrementing count QueueGrowthCheck to ' . $location['QueueGrowthCount'] . ' for: ' . $key);
                    }elseif($location['PendingTests']<$previousLocation['PendingTests'] && $location['QueueGrowthCount']>0) {
                        logOutput('[INFO] [updateQueueProcessRate] Decrementing count QueueGrowthCheck for: ' . $key);
//              $location['QueueGrowthCount'] = $previousLocation['QueueGrowthCount']-1;
                        $location['QueueGrowthCount'] = '0';
                    }
                }
                $location['QueueGrowthCheckTimeStamp'] = $currentTime;
                $locationInformation[$key]             = $location;
//        } else {
//          logOutput('[INFO] [updateQueueProcessRate] Skipping QueueGrowthCheck: interval not passed for : '.$key);
//           Keep previous info it update time hasn't passed
//          $locationInformation[$key] = $previousLocation;
//        }
            }

            // Write current status out
            writeIniFile($locationInformation, $queueStatusFilename, true);
        }
    }catch (Exception $e) {
        logOutput('[ERROR] [updateQueueProcessRate] Exception : ' . $e->getMessage());
    }
}

/*
 * Check the ratio of runrate to tester
 * Currently hard coded for a ratio of 40 "job submissions" for each tester per hour
 *
 */
function checkTesterRatioAndEmailAlert() {
    $queueStatusFilename     = './QueueStatus.ini';
    $queueStatus             = parse_ini_file($queueStatusFilename, true);
    $configTable             = Doctrine_Core::getTable('WPTMonitorConfig');
    $config                  = $configTable->find(1);
    $siteContactEmailAddress = $config['SiteContactEmailAddress'];
    // Check runrate to test ration and alert if necessary
    // Currently hard coded to alert if ration exceeds 40:1
    // TODO: Make configurable, or improve method of determining optimum run rate
    $locations = getLocationInformation();
    $message   = "";
    foreach ($locations as $location) {
        $id        = $location['id'];
        $queueStat = $queueStatus["'" . $id . "'"];
        if(empty( $location ) || !array_key_exists('id', $location) || empty( $location['id'] )) {
            continue;
        }
        $runRate    = $location['runRate'];
        $agentCount = $location['AgentCount'];
        if(empty( $agentCount )) {
            $agentCount = '0';
        }
        $requiredAdditionalTesters = ( $runRate/TARGET_RUN_RATE_RATIO )-$agentCount;
        if($location['runRate']) {
            if($location['AgentCount']<1 || ( $location['runRate']/$location['AgentCount']>TARGET_RUN_RATE_RATIO )) {
                // Only alert once per hour
                if(!array_key_exists('LastTesterRatioAlertTime', $queueStat) ||
                    ( current_seconds()>$queueStat['LastTesterRatioAlertTime']+3600 )
                ) {
                    logOutput('[INFO] [checkTesterRatioAndEmailAlert] LastTesterRatioAlertTime for: ' . $id . ' has not alerted since: ' . $queueStat['LastTesterRatioAlertTime']);
                    $message = "--------------------------------------------\n";
                    $message .= "Runrate ratio insufficient for location: " . $id . "\n";
                    $message .= "Runrate: " . $runRate . "\n";
                    $message .= "Testers: " . $agentCount . "\n";
                    $message .= "Timestamp: " . timestamp() . "\n";
                    if($agentCount) {
                        $message .= "Ratio: " . $runRate/$agentCount . "\n";
                    }else {
                        $message .= "Ratio: NO TESTERS SERVING THIS REGION\n";
                    }
                    $message .= "--------------------------------------------\n";
                    $message .= "Target Ratio: " . TARGET_RUN_RATE_RATIO . "\n";
                    $message .= "Please add " . ceil($requiredAdditionalTesters) . " additional agents for this location.";
                    // Update time stamp
                    $queueStatus["'" . $id . "'"]['LastTesterRatioAlertTime'] = current_seconds();
                    writeIniFile($queueStatus, $queueStatusFilename, true);
                }else {
                    logOutput('[INFO] [checkTesterRatioAndEmailAlert] LastTesterRatioAlertTime for: ' . $id . ' alert interval has not passed. Last Alert: ' . $queueStat['LastTesterRatioAlertTime']);
                }
            }
        }
    }
    if($message) {
        sendEmailAlert($siteContactEmailAddress, $message);
        logOutput("[INFO] [checkTesterRatioAndEmailAlert] Alert message sent:\n" . $message);
        echo $message;
    }
}

function updateLocations($id) {
    try {

        $q    = Doctrine_Query::create()
                              ->from('WPTHost h')
                              ->where('h.Id= ?', $id);
        $host = $q->fetchOne();
        $q->free(true);

        if($host) {
            $locationsArray = getLocationInformation($host['HostURL']);
        }else {
            echo "no host";
            exit;
        }

        // Update information from getLocations call
        foreach ($locationsArray as $key => $loc) {
            $locationId  = $loc['id'];
            $q           = Doctrine_Query::create()
                                         ->from('WPTLocation l')
                                         ->where('l.Location= ?', $locationId)
                                         ->andWhere('l.WPTHostId = ?', $id);
            $wptLocation = $q->fetchOne();
            $q->free(true);
            if(!$wptLocation) {
                $wptLocation = new WPTLocation();
            }
            $wptLocation['Label']     = $loc['Label'];
            $wptLocation['Location']  = $loc['id'];
            $wptLocation['WPTHostId'] = $id;
            $wptLocation['Active']    = true;
            $wptLocation['Valid']     = true;
            $wptLocation['Browser']   = $loc['Browser'];
            $wptLocation->save();
        }
        // Check for invalid locations
        $q         = Doctrine_Query::create()
                                   ->from('WPTLocation l')
                                   ->where('l.WPTHostId= ?', $id);
        $locations = $q->execute();
        $q->free(true);
        foreach ($locations as $key => $location) {
            $id = $location['Location'];
            foreach ($locationsArray as $locArray) {
                if($locArray['id'] == $id) {
                    $location['Valid'] = true;
                    break;
                }
                $location['Valid'] = false;
            }
            $location->save();
        }
        unset( $locations );
    }catch (Exception $e) {
        print $e;
        exit;
    }
}

?>
